﻿using GameLibrary;
using SynapseGaming.SunBurn.Framework.Core;
using SynapseGaming.SunBurn.Framework.Graphics;
using System;
using System.Windows.Forms;
using System.IO;

using SBColor = SynapseGaming.SunBurn.Framework.Primitives.Color;
using WFColor = System.Drawing.Color;

using Bitmap = System.Drawing.Bitmap;
using DialogResult = System.Windows.Forms.DialogResult;
using SynapseGaming.SunBurn.Framework.Graphics.Textures;

namespace WindowsDesktop
{
    public partial class MainForm : Form
    {
        private Platform platform;
        private PresentationParameters presentationParameters;

        private SimpleGameScreen gameScreen;

        private GameTime gameTime;

        private Timer timer;
        private DateTime gameCurrentTime;
        private DateTime gameStartTime;

        private BaseGameProgram baseGame;

        private SBColor[] colorMapColors;
        private Bitmap colorMapImage;

        private SBColor[] heightMapColors;
        private Bitmap heightMapImage;

        public MainForm()
        {
            InitializeComponent();
            tbTextureToDraw.SelectedIndex = 0;

            timer = new Timer();
            timer.Interval = 16;
            timer.Tick += timer_Tick;

            gameTime = new GameTime();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            presentationParameters = new PresentationParameters();

            presentationParameters.BackBufferWidth = ClientSize.Width;
            presentationParameters.BackBufferHeight = ClientSize.Height;
            presentationParameters.DepthStencilFormat = DepthFormat.Depth24Stencil8;

            platform = new SynapseGaming.SunBurn.Framework.Driver.DX11.DX11Platform(GraphicsProfile.Any);
            platform.SetTargetWindow(Viewport, presentationParameters);

            platform.InputDevice.MouseState.Visible = true;

            baseGame = new BaseGameProgram();
            baseGame.OnPlatformInitializationComplete();

            gameScreen = new SimpleGameScreen();
            platform.SetGameScreen(gameScreen);

            gameScreen.HeightMap.ColorMapChanged += HeightMap_ColorMapChanged;
            gameScreen.HeightMap.HeightMapChanged += HeightMap_HeightMapChanged;

            gameScreen.HeightMap.CreateNewHeightMap(MapSize.Medium);

            HeightMap.Image = TextureToBitmap(gameScreen.HeightMap.Texture);
            ColorMap.Image = TextureToBitmap(gameScreen.HeightMap.ColorMap);

            Texture1.Image = TextureToBitmap(gameScreen.HeightMap.Materials[1].Texture);
            Texture2.Image = TextureToBitmap(gameScreen.HeightMap.Materials[2].Texture);
            Texture3.Image = TextureToBitmap(gameScreen.HeightMap.Materials[3].Texture);
            Texture4.Image = TextureToBitmap(gameScreen.HeightMap.Materials[4].Texture);

            timer.Enabled = true;
            gameStartTime = DateTime.Now;

            tbCursorSize.Maximum = (int)gameScreen.HeightMap.Texture.Width / 2;
            tbCursorSize.TickFrequency = tbCursorSize.Maximum / 10;
        }

        private void HeightMap_HeightMapChanged(object sender, EventArgs e)
        {
            if (heightMapColors == null || heightMapImage.Width != gameScreen.HeightMap.Texture.Width)
            {
                heightMapColors = new SBColor[gameScreen.HeightMap.Texture.Width
                    * gameScreen.HeightMap.Texture.Height];
            }

            gameScreen.HeightMap.Texture.GetData(heightMapColors);

            if (heightMapImage == null || heightMapImage.Width != gameScreen.HeightMap.Texture.Width)
            {
                heightMapImage = new Bitmap(gameScreen.HeightMap.Texture.Width,
                    gameScreen.HeightMap.Texture.Height);
            }

            for (int x = 0; x < gameScreen.HeightMap.Texture.Width; x++)
            {
                for (int y = 0; y < gameScreen.HeightMap.Texture.Height; y++)
                {
                    SBColor color =
                        heightMapColors[x + y * gameScreen.HeightMap.Texture.Width];

                    heightMapImage.SetPixel(x, y,
                        WFColor.FromArgb(color.A,
                                                      color.R,
                                                      color.G,
                                                      color.B));
                }
            }

            HeightMap.Image = heightMapImage;
            lblHeightMapSize.Text = heightMapImage.Width.ToString() + "x" + heightMapImage.Height.ToString();

            tbCursorSize.Maximum = (int)gameScreen.HeightMap.Texture.Width / 2;
            tbCursorSize.TickFrequency = tbCursorSize.Maximum / 10;
        }
        private void HeightMap_ColorMapChanged(object sender, EventArgs e)
        {
            if (colorMapColors == null || colorMapImage.Width != gameScreen.HeightMap.ColorMap.Width)
            {
                colorMapColors = new SBColor[gameScreen.HeightMap.ColorMap.Width
                    * gameScreen.HeightMap.ColorMap.Height];
            }

            gameScreen.HeightMap.ColorMap.GetData(colorMapColors);

            if (colorMapImage == null || colorMapImage.Width != gameScreen.HeightMap.ColorMap.Width)
            {
                colorMapImage = new Bitmap(gameScreen.HeightMap.ColorMap.Width,
                    gameScreen.HeightMap.ColorMap.Height);
            }

            for (int x = 0; x < gameScreen.HeightMap.ColorMap.Width; x++)
            {
                for (int y = 0; y < gameScreen.HeightMap.ColorMap.Height; y++)
                {
                    SBColor color =
                        colorMapColors[x + y * gameScreen.HeightMap.ColorMap.Width];

                    colorMapImage.SetPixel(x, y,
                        WFColor.FromArgb(color.A,
                                                      color.R,
                                                      color.G,
                                                      color.B));
                }
            }

            ColorMap.Image = colorMapImage;
            lblColorMapSize.Text = colorMapImage.Width.ToString() + "x" + colorMapImage.Height.ToString();
        }

        private Bitmap TextureToBitmap(Texture2D texture)
        {
            SBColor[] colors = new SBColor[texture.Width * texture.Height];
            texture.GetData(colors);

            Bitmap bitmap = new Bitmap(texture.Width, texture.Height);

            for (int x = 0; x < texture.Width; x++)
            {
                for (int y = 0; y < texture.Height; y++)
                {
                    SBColor color = colors[x + y * texture.Width];

                    bitmap.SetPixel(x, y, WFColor.FromArgb(color.A,
                                                           color.R,
                                                           color.G,
                                                           color.B));
                }
            }

            colors = null;

            return bitmap;
        }
        private Texture2D BitmapToTexture(Bitmap bitmap)
        {
            SBColor[] colors = new SBColor[bitmap.Width * bitmap.Height];

            for (int x = 0; x < bitmap.Width; x++)
            {
                for (int y = 0; y < bitmap.Height; y++)
                {
                    WFColor bitColor = bitmap.GetPixel(x, y);
                    SBColor color = new SBColor(bitColor.R, bitColor.G, bitColor.B, bitColor.A);

                    colors[x + y * bitmap.Width] = color;
                }
            }

            Texture2D texture = Platform.Instance.GraphicsDevice.CreateTexture2D(
                bitmap.Width, bitmap.Height, false, true, SurfaceFormat.Color, true);

            texture.SetData(colors);

            colors = null;

            return texture;
        }

        private Texture2D LoadTexture(string fileName)
        {
            return new SynapseGaming.SunBurn.Framework.Driver.DX11.Graphics.DX11Texture2D((
                Platform.Instance.GraphicsDevice as SynapseGaming.SunBurn.Framework.Driver.DX11.Graphics.DX11GraphicsDevice),
                SharpDX.Direct3D11.Texture2D.FromFile((Platform.Instance.GraphicsDevice as
                        SynapseGaming.SunBurn.Framework.Driver.DX11.Graphics.DX11GraphicsDevice).NativeObject, openTextureDialog.FileName)
                        as SharpDX.Direct3D11.Texture2D);
        }

        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            base.OnFormClosed(e);

            timer.Enabled = false;
            baseGame.Dispose();
            platform.Dispose();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            DateTime now = DateTime.Now;
            gameTime.ElapsedGameTime = now - gameCurrentTime;
            gameTime.TotalGameTime = now - gameStartTime;
            gameCurrentTime = now;

            gameScreen.IsActive = ContainsFocus;

            platform.RunManually.Update(gameTime);
            platform.RunManually.Draw(gameTime);
        }

        private void btnRaise_CheckedChanged(object sender, EventArgs e)
        {
            if (btnRaise.Checked)
            {
                btnLower.Checked = false;
                btnSmooth.Checked = false;
                btnPrecision.Checked = false;
                btnPaint.Checked = false;
                btnFlatten.Checked = false;

                gameScreen.ToolType = ToolType.Raise;
            }
        }
        private void btnLower_CheckedChanged(object sender, EventArgs e)
        {
            if (btnLower.Checked)
            {
                btnRaise.Checked = false;
                btnSmooth.Checked = false;
                btnPrecision.Checked = false;
                btnPaint.Checked = false;
                btnFlatten.Checked = false;

                gameScreen.ToolType = ToolType.Lower;
            }
        }
        private void btnSmooth_CheckedChanged(object sender, EventArgs e)
        {
            if (btnSmooth.Checked)
            {
                btnRaise.Checked = false;
                btnLower.Checked = false;
                btnPrecision.Checked = false;
                btnPaint.Checked = false;
                btnFlatten.Checked = false;

                gameScreen.ToolType = ToolType.Smooth;
            }
        }
        private void btnPrecision_CheckedChanged(object sender, EventArgs e)
        {
            if (btnPrecision.Checked)
            {
                btnRaise.Checked = false;
                btnSmooth.Checked = false;
                btnLower.Checked = false;
                btnPaint.Checked = false;
                btnFlatten.Checked = false;

                gameScreen.ToolType = ToolType.Precision;
            }
        }
        private void btnPaint_CheckedChanged(object sender, EventArgs e)
        {
            if (btnPaint.Checked)
            {
                btnRaise.Checked = false;
                btnSmooth.Checked = false;
                btnPrecision.Checked = false;
                btnLower.Checked = false;
                btnFlatten.Checked = false;

                gameScreen.ToolType = ToolType.Paint;
            }
        }
        private void btnFlatten_CheckedChanged(object sender, EventArgs e)
        {
            if (btnFlatten.Checked)
            {
                btnRaise.Checked = false;
                btnSmooth.Checked = false;
                btnPrecision.Checked = false;
                btnPaint.Checked = false;
                btnLower.Checked = false;

                gameScreen.ToolType = ToolType.Flatten;
            }
        }

        private void tbCursorStrength_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.CursorStrength = tbCursorStrength.Value;
        }
        private void tbCursorSize_Scroll(object sender, EventArgs e)
        {
            gameScreen.HeightMap.CursorSize = tbCursorSize.Value;
        }

        private void tbMaximumHeight_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.UpdateMaxHeight((float)tbMaximumHeight.Value);
        }
        private void tbCellSize_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.UpdateCellSize((float)tbCellSize.Value);
        }

        private void HeightMap_Click(object sender, EventArgs e)
        {
            if (openTextureDialog.ShowDialog() == DialogResult.OK)
            {
                using (Bitmap bitmap = new Bitmap(openTextureDialog.FileName))
                {
                    gameScreen.HeightMap.LoadHeightMap(BitmapToTexture(bitmap));
                }

                lblHeightmapPath.Text = Path.GetFileName(openTextureDialog.FileName);
                lblHeightmapPath.Tag = openTextureDialog.FileName;
            }
        }
        private void ColorMap_Click(object sender, EventArgs e)
        {
            if (openTextureDialog.ShowDialog() == DialogResult.OK)
            {
                using (Bitmap bitmap = new Bitmap(openTextureDialog.FileName))
                {
                    gameScreen.HeightMap.LoadColormap(BitmapToTexture(bitmap));
                }

                lblColorMapPath.Text = Path.GetFileName(openTextureDialog.FileName);
                lblColorMapPath.Tag = openTextureDialog.FileName;
            }
        }

        private void Texture1_Click(object sender, EventArgs e)
        {
            if (openTextureDialog.ShowDialog() == DialogResult.OK)
            {
                gameScreen.HeightMap.Materials[1].Texture = LoadTexture(openTextureDialog.FileName);
                gameScreen.HeightMap.Materials[1].TextureName = Path.GetFileName(openTextureDialog.FileName);

                Texture1.Image = new Bitmap(openTextureDialog.FileName);
            }
        }
        private void Texture2_Click(object sender, EventArgs e)
        {
            if (openTextureDialog.ShowDialog() == DialogResult.OK)
            {
                gameScreen.HeightMap.Materials[2].Texture = LoadTexture(openTextureDialog.FileName);
                gameScreen.HeightMap.Materials[2].TextureName = Path.GetFileName(openTextureDialog.FileName);

                Texture2.Image = new Bitmap(openTextureDialog.FileName);
            }
        }
        private void Texture3_Click(object sender, EventArgs e)
        {
            if (openTextureDialog.ShowDialog() == DialogResult.OK)
            {
                gameScreen.HeightMap.Materials[3].Texture = LoadTexture(openTextureDialog.FileName);
                gameScreen.HeightMap.Materials[3].TextureName = Path.GetFileName(openTextureDialog.FileName);

                Texture3.Image = new Bitmap(openTextureDialog.FileName);
            }
        }
        private void Texture4_Click(object sender, EventArgs e)
        {
            if (openTextureDialog.ShowDialog() == DialogResult.OK)
            {
                gameScreen.HeightMap.Materials[4].Texture = LoadTexture(openTextureDialog.FileName);
                gameScreen.HeightMap.Materials[4].TextureName = Path.GetFileName(openTextureDialog.FileName);

                Texture4.Image = new Bitmap(openTextureDialog.FileName);
            }
        }

        private void tbScale1_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.Materials[1].Scale = (float)tbScale1.Value;
        }
        private void tbScale2_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.Materials[2].Scale = (float)tbScale2.Value;
        }
        private void tbScale3_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.Materials[3].Scale = (float)tbScale3.Value;
        }
        private void tbScale4_ValueChanged(object sender, EventArgs e)
        {
            gameScreen.HeightMap.Materials[4].Scale = (float)tbScale4.Value;
        }

        private void btnSmoothHeightMap_Click(object sender, EventArgs e)
        {
            gameScreen.HeightMap.SmoothHeightMap();
        }
        private void btnSmoothColorMap_Click(object sender, EventArgs e)
        {
            gameScreen.HeightMap.NormalizeColorMap();
        }

        private void tbTextureToDraw_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (gameScreen != null)
            {
                gameScreen.TextureIndex = tbTextureToDraw.SelectedIndex;
            }
        }

        private void btnSaveHeightMap_Click(object sender, EventArgs e)
        {
            if (saveTextureDialog.ShowDialog() == DialogResult.OK)
            {
                TextureToBitmap(gameScreen.HeightMap.Texture).Save(
                    saveTextureDialog.FileName, System.Drawing.Imaging.ImageFormat.Png);
            }
        }

        private void btnSaveColorMap_Click(object sender, EventArgs e)
        {
            if (saveTextureDialog.ShowDialog() == DialogResult.OK)
            {
                TextureToBitmap(gameScreen.HeightMap.ColorMap).Save(
                    saveTextureDialog.FileName, System.Drawing.Imaging.ImageFormat.Png);
            }
        }

        private void btnResetHeightMap_Click(object sender, EventArgs e)
        {
            using (SizeDialog sizeDialog = new SizeDialog())
            {
                if (sizeDialog.ShowDialog() == DialogResult.OK)
                {
                    gameScreen.HeightMap.CreateNewHeightMap(sizeDialog.SelectedSize);
                }
            }
        }
        private void btnResetColorMap_Click(object sender, EventArgs e)
        {
            using (SizeDialog sizeDialog = new SizeDialog())
            {
                if (sizeDialog.ShowDialog() == DialogResult.OK)
                {
                    gameScreen.HeightMap.CreateNewColorMap(sizeDialog.SelectedSize);
                }
            }
        }

        private void Viewport_Click(object sender, EventArgs e)
        {
            
        }
    }
}
